package com.weacar.weixin.service;

import com.weacar.weixin.controller.SendBonusBean;
import com.weacar.weixin.dao.BonusDao;
import com.weacar.weixin.model.Bonus;
import com.weacar.weixin.util.LogUtils;
import com.weacar.weixin.util.MD5;
import com.weacar.weixin.util.MessageUtil;
import com.weacar.weixin.util.RandomUtil;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.net.ssl.SSLContext;
import java.io.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicLong;

/**
 * User: babyface <xulongfa@weacar.com>
 * Date: 15/10/14
 * Time: 22:54
 */
@Service("bonusService")
public class BonusServiceImpl implements BonusService {

    private static final Logger log = LoggerFactory.getLogger(BonusServiceImpl.class);


    @Value("${weixin.bonus.min:100}")
    private Integer min;
    @Value("${weixin.bonus.max:110}")
    private Integer max;

    private Date startAt = new Date();
    private Object lock = new Object();
    private AtomicLong count = new AtomicLong();

    //去重
    private static final List<String> existsCodeList = new CopyOnWriteArrayList<String>();

    @Autowired
    private BonusDao bonusDao;


    @Override
    public Bonus initBonus(String fromUserName, String phone) {
        try {
            Bonus bonus = new Bonus();
            bonus.setPhone(phone);
            bonus.setStatus("created");
            bonus.setUserName(fromUserName);
            bonus.setConfirmCode(confirmCode(fromUserName));
            bonusDao.save(bonus);
            return bonus;
        } catch (Exception e) {
            LogUtils.errorException(log, "userName="+fromUserName +",phone="+phone, e);
            throw new InternalServiceException("Init bonus error!");
        }

    }

    private String confirmCode(String fromUserName) {
        for (int i=0; i<5; i++) {
            Integer code = ThreadLocalRandom.current().nextInt(100000, 999999);
            //微信号+验证码唯一
            if (!existsCodeList.contains(fromUserName+code.toString())) {
                existsCodeList.add(fromUserName+code.toString());
                return code.toString();
            }
        }
        throw new RuntimeException("Confirm code generate error!");
    }

    @Override
    public Optional<Bonus> getBonusByUser(String fromUserName) {
        try {
            Bonus bonus = bonusDao.queryByUser(fromUserName);
            if (bonus == null) {
                return Optional.empty();
            }
            return Optional.of(bonus);
        } catch (Exception e) {
            LogUtils.errorException(log, "userName="+fromUserName, e);
            throw new InternalServiceException("Query bonus error!");
        }
    }


    @Override
    public Optional<String> sendBonus(Bonus bonus) {
        synchronized (lock) {
            float avgSpeed = count.get() / (float)((System.currentTimeMillis() - startAt.getTime()) / 1000L);
            log.info("-----------> " + avgSpeed);
            if(avgSpeed > 0.4444652381) {
                return Optional.of("小伙伴们有点太欢乐, 稍后再试吧 ==> 网车, 你的好车, 触网可得(www.weacar.com)");
            }
        }

        try {
            String msg = sendBonusInternal(bonus.getUserName());
            if (msg == null) {
                bonus.setStatus("done");
                bonusDao.update(bonus);
                count.incrementAndGet();
                return Optional.of("网车, 你的好车, 触网可得(www.weacar.com), 谢谢你的关注, 以后还有更多精彩活动, 喵~~");
            } else {
                return Optional.of(msg);
            }
        } catch (Exception e) {
            LogUtils.errorException(log, bonus.toString(), e);
            return Optional.of("呜呜呜, 红包没发成功, 稍后再试吧, 喵~~~");
        }
    }

    @Override
    public Optional<Bonus> getBonusByPhone(String phone) {
        try {
            Bonus bonus = bonusDao.queryByPhone(phone);
            if (bonus == null) {
                return Optional.empty();
            }
            return Optional.of(bonus);
        } catch (Exception e) {
            LogUtils.errorException(log, "phone="+phone, e);
            throw new InternalServiceException("Query bonus error!");
        }
    }

    private String sendBonusInternal(String openid) throws KeyStoreException, IOException, CertificateException,
            NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException, DocumentException {

        // 获取uuid作为随机字符串
        String nonce_str = UUID.randomUUID().toString().replace("-", "");
        //商户号
        String mch_id = "1276302501";
        //商户订单号
        String code = RandomUtil.createCode(10);
        String today = new SimpleDateFormat("yyyyMMdd").format(new Date());
        String mch_billno = mch_id + today + code;
        //公众账号appid
        String wxappid = "wxa8b6e1f3587adb8c";
        //提供方名称
        String nick_name = "网车";
        //红包发送者名称 商户名称
        String send_name = "网车";
        //用户openid
        String re_openid = openid;
        //付款金额 分
        int total_amount = RandomUtil.getRandom(min, max);
        //最小红包金额，单位分
        int min_value = total_amount;
        //最大红包金额，单位分
        int max_value = total_amount;
        //红包収放总人数
        int total_num = 1;
        //红包祝福语
        String wishing = "恭喜发财";
        //调用接口的机器 Ip 地址
        String client_ip = "121.41.122.138";
        //活劢名称
        String act_name = "网车-关注送红包";
        //备注
        String remark = "网车-关注送红包";
        //商户logo的url（暂未开放）
        String logo_imgurl = "wxa8b6e1f3587adb8c";
        //分享文案（暂未开放）
        String share_content = "wxa8b6e1f3587adb8c";
        //分享链接（暂未开放）
        String share_url = "wxa8b6e1f3587adb8c";
        //分享的图片url（暂未开放）
        String share_imgurl = "wxa8b6e1f3587adb8c";

        SendBonusBean sendBonusBean = new SendBonusBean();

        sendBonusBean.setNonce_str(nonce_str);
        sendBonusBean.setMch_billno(mch_id + today + code);
        sendBonusBean.setMch_id(mch_id);
        sendBonusBean.setWxappid(wxappid);
        sendBonusBean.setNick_name(nick_name);
        sendBonusBean.setSend_name(send_name);
        sendBonusBean.setRe_openid(re_openid);
        sendBonusBean.setTotal_amount(String.valueOf(total_amount));
        sendBonusBean.setMin_value(String.valueOf(min_value));
        sendBonusBean.setMax_value(String.valueOf(max_value));
        sendBonusBean.setTotal_num(String.valueOf(total_num));
        sendBonusBean.setWishing(wishing);
        sendBonusBean.setClient_ip(client_ip);
        sendBonusBean.setAct_name(act_name);
        sendBonusBean.setRemark(remark);



        //把请求参数打包成数组
        Map<String, String> paramMap = new HashMap<String, String>();
        paramMap.put("nonce_str", nonce_str);
        paramMap.put("mch_id", mch_id);
        paramMap.put("mch_billno", mch_billno);
        paramMap.put("wxappid", wxappid);
        paramMap.put("nick_name", nick_name);
        paramMap.put("send_name", send_name);
        paramMap.put("re_openid", re_openid);
        paramMap.put("total_amount", String.valueOf(total_amount));
        paramMap.put("min_value", String.valueOf(min_value));
        paramMap.put("max_value", String.valueOf(max_value));
        paramMap.put("total_num", String.valueOf(total_num));
        paramMap.put("wishing", wishing);
        paramMap.put("client_ip", client_ip);
        paramMap.put("act_name", act_name);
        paramMap.put("remark", remark);
        String queryString = this.createLinkString(paramMap); //把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        String key = "&key=8192006250b4c092ec3302edc3e69f6a"; //商户支付密钥
        String sign = MD5.sign(queryString, key, "utf-8").toUpperCase();

        sendBonusBean.setSign(sign);

        String requestXml = MessageUtil.messageToXml(sendBonusBean);
        //打印respXml发现，得到的xml中有“__”不对，应该替换成“_”
        requestXml = requestXml.replace("__", "_");
        log.error("request xml=" + requestXml);

        KeyStore keyStore  = KeyStore.getInstance("PKCS12");
        FileInputStream instream = new FileInputStream(new File("/home/apiclient_cert.p12")); //此处为证书所放的绝对路径

        try {
            keyStore.load(instream, mch_id.toCharArray());
        } finally {
            instream.close();
        }


        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, mch_id.toCharArray())
                .build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext,
                new String[] { "TLSv1" },
                null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        // 将解析结果存储在HashMap中
        Map<String, String> map = new HashMap<String, String>();
        try {
            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack");
            StringEntity reqEntity  = new StringEntity(requestXml, "utf-8");
            // 设置类型
            reqEntity.setContentType("application/x-www-form-urlencoded");
            httpPost.setEntity(reqEntity);
            log.error("executing request:" + httpPost.getRequestLine());

            CloseableHttpResponse response = httpclient.execute(httpPost);
            try {
                HttpEntity entity = response.getEntity();
                log.error("response status=" + response.getStatusLine());
                if (entity != null) {

                    // 从request中取得输入流
                    InputStream inputStream = entity.getContent();
                    // 读取输入流
                    SAXReader reader = new SAXReader();
                    Document document = reader.read(inputStream);
                    // 得到xml根元素
                    Element root = document.getRootElement();
                    // 得到根元素的所有子节点
                    List<Element> elementList = root.elements();

                    // 遍历所有子节点
                    for (Element e : elementList)
                        map.put(e.getName(), e.getText());

                    // 释放资源
                    inputStream.close();
                }
                EntityUtils.consume(entity);
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }

        StringBuffer sb = new StringBuffer();
        for(String key1 : map.keySet()) {
            sb.append(key1).append("=").append(map.get(key1)).append("&");
        }
        log.error("response body = " + sb.toString());
        // 返回状态码
        String returnCode = map.get("return_code");
        // 返回信息
        String returnMsg = map.get("return_msg");
        // 业务结果
        String resultCode = map.get("result_code");
        // 错误代码
        String errCode = map.get("err_code");
        // 错误代码描述
        String errCodeDes = map.get("err_code_des");

        /**
         * 根据以上返回码进行业务逻辑处理
         */
        if ("NOTENOUGH".equals(errCode)) {
            return "小伙伴们太配合了, 红包发完了:), 等下再来哦~";
        }

        if ("SENDNUM_LIMIT".equals(errCode)) {
            return "亲, 红包每人只能领取一次哦~";
        }

        if("FAIL".equals(returnCode)) {
            log.error("connect error!");
            return "呜呜呜, 红包没发成功, 稍后再试吧, 喵~~~";
        }

        if("FAIL".equals(resultCode)) {
            log.error("send bonus error,errorCode=" + errCode +", errorMsg=" + errCodeDes );
            return "亲, 微信后台告诉您: " + errCodeDes;
        }

        return null;
    }

    /**
     * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
     * @param params 需要排序并参与字符拼接的参数组
     * @return 拼接后字符串
     */
    private String createLinkString(Map<String, String> params) {

        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        StringBuffer queryString = new StringBuffer();

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);

            if (i == keys.size() - 1) {//拼接时，不包括最后一个&字符
                queryString.append(key).append("=").append(value);
            } else {
                queryString.append(key).append("=").append(value).append("&");
            }
        }

        return queryString.toString();
    }
}
